package app

import (
	"context"
	// "encoding/json"
	"errors"
	"fmt"

	// "io/ioutil"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
	"go.uber.org/zap"
	"originforce.club/bilin-mock/conf"
	"originforce.club/bilin-mock/internal/dao"
	sqlc "originforce.club/bilin-mock/internal/db/sqlc"
	appMiddleware "originforce.club/bilin-mock/internal/rest/middlewares"
	appRender "originforce.club/bilin-mock/internal/rest/render"
	"originforce.club/bilin-mock/internal/util"
)

type AppApiRouter struct {
	slog *zap.SugaredLogger
	dao  *db.PgDao
	vars *conf.VarsSection
	r    chi.Router
}

// New initialization
func New(
	slog *zap.SugaredLogger,
	dao *db.PgDao,
	vars *conf.VarsSection,
) (r chi.Router) {
	s := &AppApiRouter{
		slog: slog.With("module", "AppApiRouter"),
		dao:  dao,
		vars: vars,
		r:    chi.NewRouter(),
	}
	s.register()
	return s.r
}

func (s *AppApiRouter) register() {
	s.r.Use(s.handleParentCtx)
	s.r.With(appMiddleware.WithPager(s.slog, s.vars)).Get("/", s.listApis)
	s.r.Post("/", s.createApi)
	s.r.Route("/{id}", func(r chi.Router) {
		r.Use(s.handleCtx)
		r.Get("/", s.getApi)
		r.Put("/", s.updateApi)
		r.Put("/unactive", s.unactiveApi)
		r.Put("/active", s.activeApi)
	})

}
func (s *AppApiRouter) handleParentCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		appID := chi.URLParam(r, "app_id")
		if util.IsEmpty(appID) {
			render.Render(w, r, appRender.ErrInvalidRequest(fmt.Errorf("app_id")))
			return
		}
		s.slog.Infof("app_id=>%s", appID)
		ctx := context.WithValue(r.Context(), "app_id", appID)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func (s *AppApiRouter) handleCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_apiID := chi.URLParam(r, "id")
		if util.IsEmpty(_apiID) {
			render.Render(w, r, appRender.ErrInvalidRequest(fmt.Errorf("id")))
			return
		}
		s.slog.Infof("id=>%s", _apiID)
		apiID, err := strconv.Atoi(_apiID)
		if err != nil {
			render.Render(w, r, appRender.ErrInvalidRequest(fmt.Errorf("id")))
			return
		}
		ctx := context.WithValue(r.Context(), "id", apiID)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func (s *AppApiRouter) listApis(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	appID := ctx.Value("app_id").(string)
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)

	apis, err := s.dao.Q.ListAppApisByAppID(ctx, sqlc.ListAppApisByAppIDParams{
		Status: 1,
		AppID:  appID,
		Offset: pagerPtr.Offset,
		Limit:  pagerPtr.Limit,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityListToRender(db.MapObjToPtr(apis))); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

type createApiRequest struct {
	*sqlc.UpsertAppApiParams
}

func (s *createApiRequest) Bind(r *http.Request) error {
	if s.UpsertAppApiParams == nil {
		return errors.New("缺少CreateAppApi字段.")
	}
	return nil
}

func (s *AppApiRouter) createApi(w http.ResponseWriter, r *http.Request) {
	s.slog.Info("-createApi-")
	data := &createApiRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrInvalidRequest(err))
		return
	}
	ctx := r.Context()
	appID := ctx.Value("app_id").(string)
	var apiPtr *sqlc.AppApi
	err := s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		data.UpsertAppApiParams.AppID = appID
		// s.slog.Infof("UpsertAppApiParams=>%+v", *data.UpsertAppApiParams)
		api, err := q.UpsertAppApi(ctx, *data.UpsertAppApiParams)
		if err != nil {
			return
		}
		apiPtr = db.ConvObjToPtr(api)
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(apiPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 获取app_api

func (s *AppApiRouter) getApi(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	api, err := s.dao.Q.GetAppApi(ctx, ctx.Value("id").(int64))
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	appID := ctx.Value("app_id").(string)
	if api.AppID != appID {
		render.Render(w, r, appRender.ErrInvalidRequest(errors.New("app_id invalid")))
		return
	}
	apiPtr := db.ConvObjToPtr(api)
	if err := render.Render(w, r, appRender.EntityToRender(apiPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 更新app
type updateApiRequest struct {
	*sqlc.UpdateAppApiParams
}

func (s *updateApiRequest) Bind(r *http.Request) error {
	if s.UpdateAppApiParams == nil {
		return errors.New("缺少UpdateAppApi字段.")
	}
	return nil
}

func (s *AppApiRouter) updateApi(w http.ResponseWriter, r *http.Request) {
	data := &updateApiRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrInvalidRequest(err))
		return
	}
	ctx := r.Context()
	data.UpdateAppApiParams.ID = ctx.Value("id").(int64)
	var apiPtr *sqlc.AppApi
	err := s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
		api, err := q.UpdateAppApi(ctx, *data.UpdateAppApiParams)
		if err != nil {
			return
		}
		apiPtr = db.ConvObjToPtr(api)
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(apiPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 停用app_api
func (s *AppApiRouter) unactiveApi(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	err := s.dao.Q.UpdateAppApiStatus(ctx, sqlc.UpdateAppApiStatusParams{
		ID:     ctx.Value("id").(int64),
		Status: 0,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 启用app_api
func (s *AppApiRouter) activeApi(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	err := s.dao.Q.UpdateAppApiStatus(ctx, sqlc.UpdateAppApiStatusParams{
		ID:     ctx.Value("id").(int64),
		Status: 1,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}
